"use strict";

// 部分函数 api 参考https://blog.csdn.net/monparadis/article/details/120760302

var canvas = document.getElementById('canvas'), // 获取画布元素
    ctx = canvas.getContext('2d'), // 建立一个2为 context 用来渲染
    w = canvas.width = window.innerWidth, // 设置画布宽度
    h = canvas.height = window.innerHeight, // 设置画布高度
    hue = 217, // 色相
    stars = [], // 星星数组
    count = 0, // 计数
    maxStars = 1500; // 星星最多数量

// 画布缓存渐变
var canvas2 = document.createElement('canvas');
var w2 = canvas2.width = 100;
var h2 = canvas2.height = 100;
var ctx2 = canvas2.getContext("2d");

// 在虚拟画布中心绘制渐变，即星星是渐变效果的，或者说是一个由内到外由深到浅的渐变圆
var gradientCache = ctx2.createRadialGradient(
    w2 / 2,
    h2 / 2,
    0,
    w2 / 2,
    h2 / 2,
    w2 / 2
);
// 规定渐变对象中的颜色和停止位置，HSL即色相、饱和度、亮度（Hue, Saturation, Lightness），这里是设置星星的效果
// 星星由两个圆组成，外面的低亮度圆，里面的高亮度圆，最终效果可以控制星星是否饱满
gradientCache.addColorStop(0.025, '#fff');
gradientCache.addColorStop(0.1, 'hsl(' + hue + ', 61%, 43%)');
gradientCache.addColorStop(0.25, 'hsl(' + hue + ', 64%, 13%)');
gradientCache.addColorStop(1, 'transparent');
// 设置用于填充的颜色、渐变、模式
ctx2.fillStyle = gradientCache;
// 起始一条路径
ctx2.beginPath();
// 创建一个圆心为 (w_2/2,h_2/2)，半径为 w_2/2 的完整的圆
ctx2.arc(w2 / 2, h2 / 2, w2 / 2, 0, Math.PI * 2);
// 进行填充
ctx2.fill();

// 至此绘制渐变结束

/**
 * 生成 min~max 之间的随机数
 * @param min 左端点
 * @param max 右端点
 * @returns {number} 一个随机数
 */
function random(min, max) {
    if (arguments.length < 2) {
        max = min;
        min = 0;
    }

    if (min > max) {
        var hold = max;
        max = min;
        min = hold;
    }

    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * 计算最大轨道，长或宽中的最大值的 sqrt(2)/2 倍，相当于长或宽作为正方形一边时，对角线长度的二分之一
 * @param x
 * @param y
 * @returns {number}
 */
function maxOrbit(x, y) {
    var max = Math.max(x, y),
        diameter = Math.round(Math.sqrt(max * max + max * max)); // round 指返回一个数字四舍五入后最接近的整数
    return diameter / 2;
}

/**
 * 星星对象
 * @constructor 星星的构造函数
 */
var Star = function () {
    this.orbitRadius = random(maxOrbit(w, h));// 轨道最大半径
    this.radius = random(60, this.orbitRadius) / 10; // 计算真实半径，即 60~orbitRadius 中的一个随机值
    this.orbitX = w / 2; // 轨道中心 x 坐标
    this.orbitY = h / 2; // 轨道中心 y 坐标
    this.timePassed = random(0, maxStars); // 0~1500 之间的随机数，用于生成星星的位置，类似于角度，角度随时间变化
    this.speed = random(this.orbitRadius) / 100000; // 旋转速度（运动速度），与轨道大小有关
    this.alpha = random(2, 10) / 10; // 透明度

    count++; // 记录星星数
    stars[count] = this; // 将星星对象放到星星数组里面
}

/**
 * 绘制星星
 */
Star.prototype.draw = function () {
    var x = Math.sin(this.timePassed) * this.orbitRadius + this.orbitX, // 星星周围还有星星，坐标为 ( x + r * {\sin}{\theta}, y + r * {\cos}{\theta})
        y = Math.cos(this.timePassed) * this.orbitRadius + this.orbitY,
        twinkle = random(10); // 一闪一闪亮晶晶

    // 透明度变化产生星星一闪一闪的效果，透明度变化范围在 0~1 内
    // 为了不太闪，透明度是线性变化的
    if (twinkle === 1 && this.alpha > 0) {
        this.alpha -= 0.05;
    } else if (twinkle === 2 && this.alpha < 1) {
        this.alpha += 0.05;
    }

    // 设置星星透明度
    ctx.globalAlpha = this.alpha;
    // 绘制缓存渐变
    ctx.drawImage(canvas2, x - this.radius / 2, y - this.radius / 2, this.radius, this.radius); // 绘制图像，具体参数详见 https://www.runoob.com/tags/canvas-drawimage.html
    this.timePassed += this.speed;
}

// 生成1500个星星
for (var i = 0; i < maxStars; i++) {
    new Star();
}

/**
 * 动画绘制
 */
function animation() {
    ctx.globalCompositeOperation = 'source-over'; // 不好描述效果，意会，可以参见 https://www.w3school.com.cn/tags/canvas_globalcompositeoperation.asp
    ctx.globalAlpha = 0.9; // 设置背景透明度，可以控制是否流星/彗星效果（是否拖尾），如果希望有，建议0.3-0.5
    ctx.fillStyle = 'hsla(' + hue + ', 64%, 1%, 1)'; // 可以控制背景色相、饱和度、亮度、透明度
    ctx.fillRect(0, 0, w, h) // 把以上内容填充到一个矩形中，举行大小为整个页面（当前页面），如果页面缩放了，矩形不会跟着缩放，刷新就可以恢复效果了

    ctx.globalCompositeOperation = 'lighter';
    for (var i = 1, l = stars.length; i < l; i++) {
        stars[i].draw();// 把星星数组里面的所有星星画出来
    }
    // 更新帧率
    window.requestAnimationFrame(animation);
}

animation();